/************************************************************************
Copyright 2003, 2004, 2005, 2006, 2007, 2008 Grid Technology Research Center,
National Institute of Advanced Industrial Science and Technology.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
************************************************************************/

package jp.aist.gtrc.plus.scheduler.specific.condor;

import condor.classad.*;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.LineNumberReader;
import java.util.*;

import jp.aist.gtrc.plus.reserve.ReserveException;
import jp.aist.gtrc.plus.scheduler.node.NodeInfo;
import jp.aist.gtrc.plus.scheduler.status.JobID;
import jp.aist.gtrc.plus.scheduler.sys.SchedulerException;
import org.apache.log4j.Logger;

public class Util {
	protected static final transient Logger logger = Logger.getLogger(Util.class);

	private static final String CONDOR_Q      = "condor_q";
	private static final String CONDOR_STATUS = "condor_status";
	private static final String CONDOR_HOLD   = "condor_hold";
	private static final String CONDOR_RELEASE= "condor_release";
	private static final String CONDOR_RM     = "condor_rm";
	
	private static final String PLUS_CONDOR_STATUS = "plus_condor_status";
    // returns something like
	//  ID0 NODE0 NODE1 ..
	//  ID1 NODE0 NODE1 ..
	
	private static final String PLUS_CONDOR_CONTROL = "plus_condor_control";    
	// args = --start -r reserveID -o ownerList -n nodeList [-p]
	// args = --end   -r reserveID -n nodeList [-p]
	// args = --cancel -r reserveID -n nodeList [-p]

	// ownerList and nodeList should be a comma seperated
	// string like nakada,takefusa
	
	static List<Expr> parseClassAds(LineNumberReader lnr){
		List<Expr> tmp = new ArrayList<Expr>();		
		ClassAdParser parser = new ClassAdParser(lnr, ClassAdParser.XML);
		Expr expr = parser.parse();
		
		ListExpr list = (ListExpr)expr;
		Iterator<?> iter = list.iterator();
		while (iter.hasNext()){
			tmp.add((Expr)iter.next());
		}
		
		return tmp;
	}
	
	static List<Expr> getCondorQClassAds() throws SchedulerException {
		try {
			Runtime runtime = Runtime.getRuntime();
			Process proc = runtime.exec(new String[]{CONDOR_Q, "-xml"});
			LineNumberReader lnr = 
				new LineNumberReader(new InputStreamReader(proc.getInputStream()));
			for (int i = 0; i < 5; i++)
				System.out.println(lnr.readLine()); // skip 
			return parseClassAds(lnr);
		} catch (IOException e){
			throw new SchedulerException(e);
		}
	}

	static List<Expr> getCondorStatusClassAds() throws SchedulerException {
		try {
			Runtime runtime = Runtime.getRuntime();
			Process proc = runtime.exec(new String[]{CONDOR_STATUS, "-xml"});
			LineNumberReader lnr = 
				new LineNumberReader(new InputStreamReader(proc.getInputStream()));
			for (int i = 0; i < 2; i++)
				System.out.println(lnr.readLine()); // skip 
			return parseClassAds(lnr);
		} catch (IOException e){
			throw new SchedulerException(e);
		}
	}
	
	static String stringAttr(RecordExpr rec, String key) throws SchedulerException{
	    Expr tmp = rec.lookup(key);
	    if (tmp == null)
		throw new SchedulerException(rec + " does not have attr key= " + key);
	    return ((Constant)tmp).stringValue();
	}

	static int intAttr(RecordExpr rec, String key) throws SchedulerException{
	    Expr tmp = rec.lookup(key);
	    if (tmp == null)
		throw new SchedulerException(rec + " does not have attr key= " + key);
	    return ((Constant)tmp).intValue();
	}

	static double doubleAttr(RecordExpr rec, String key) throws SchedulerException{
	    Expr tmp = rec.lookup(key);
	    if (tmp == null)
		throw new SchedulerException(rec + " does not have attr key= " + key);
	    return ((Constant)tmp).realValue();
	}

	static int exec(String... cmds)
	throws ReserveException {
		try {
			Runtime runtime = Runtime.getRuntime();
			logger.info("about to exec " + Util.join(cmds, " "));
			Process proc = runtime.exec(cmds);
			List<String> out = Util.readLines(proc.getInputStream());
			List<String> err = Util.readLines(proc.getErrorStream());
			logger.info("... output \n" + Util.join(out, "\n"));
			logger.info("... error  \n" + Util.join(err, "\n"));
			return proc.waitFor();
		} catch (IOException e){
			throw new ReserveException(e);
		} catch (InterruptedException e) {
			throw new ReserveException(e);
		}
	}

	static List<String> quote(String... cmds)
	throws ReserveException {		
		try {
			Runtime runtime = Runtime.getRuntime();
			logger.info("about to exec " + Util.join(cmds, " "));
			Process proc = runtime.exec(cmds);
			List<String> out = Util.readLines(proc.getInputStream());
			List<String> err = Util.readLines(proc.getErrorStream());
			logger.info("... output \n" + Util.join(out, "\n"));
			logger.info("... error  \n" + Util.join(err, "\n"));
			proc.waitFor();
			return out;
		} catch (IOException e){
			throw new ReserveException(e);
		} catch (InterruptedException e) {
			throw new ReserveException(e);
		}
	}
	
		
	static List<String> readLines(InputStream is) throws ReserveException {
		List<String> lines = new ArrayList<String>(); 
		LineNumberReader lnr = new LineNumberReader(new InputStreamReader(is));
		String tmp;
		try {
			while ((tmp = lnr.readLine())!= null)
				lines.add(tmp);
		} catch (IOException e) {
			throw new ReserveException(e);
		}
		return lines;
	}
	

	
	static List<String> plusCondorStatus() throws ReserveException {
		return Util.quote(PLUS_CONDOR_STATUS);
	}
	
	
	static void deleteJob(JobID jobID) throws SchedulerException {
		exec(CONDOR_RM, jobID.toString());
	}

	static void suspendJob(JobID jobID) throws SchedulerException {
		exec(CONDOR_HOLD, jobID.toString());
	}
	
	static void resumeJob(JobID jobID) throws SchedulerException {
		exec(CONDOR_RELEASE, jobID.toString());
	}
	
	static void holdJob(JobID jobID) throws SchedulerException {
		exec(CONDOR_HOLD, jobID.toString());
	}
	
	static void releaseJob(JobID jobID) throws SchedulerException {
		exec(CONDOR_RELEASE, jobID.toString());
	}
	
	static void rerunJob(JobID jobID) throws SchedulerException {
		holdJob(jobID);
		releaseJob(jobID);
	}

	static void suspendQueue(String hostname, String qname){
//		nothing to do, I hope
	}
	
	static void resumeQueue(String hostname, String qname){
//		nothing to do, I hope
	}

	private static String [] getNodeNames(Collection<NodeInfo> infos){
		List<String> nodeList = new ArrayList<String>();
		for (NodeInfo info: infos)
			nodeList.add(info.getName());
		return nodeList.toArray(new String[0]);
	}
	
	private static String join(String [] strs, String sep){
		if (strs.length == 0)
			return "";
		StringBuffer tmp = new StringBuffer(strs[0]);
		for (int i = 1; i < strs.length; i++){
			tmp.append(sep);
			tmp.append(strs[i]);
		}
		return tmp.toString();
	}

	private static String join(List<String> strs, String sep){
		if (strs.size() == 0)
			return "";
		StringBuffer tmp = new StringBuffer(strs.get(0));
		for (int i = 1; i < strs.size(); i++){
			tmp.append(sep);
			tmp.append(strs.get(i));
		}
		return tmp.toString();
	}
	
	public static void deactivateQueue(String queueName, Collection<NodeInfo> infos) 
	throws ReserveException{
		String [] nodes = getNodeNames(infos);
		List<String> cmds = new ArrayList<String>();
		cmds.add(PLUS_CONDOR_CONTROL);
		cmds.add("--end"); 
		cmds.add("-r");
		cmds.add(queueName); 
		if (nodes.length != 0){
			cmds.add("-n");
			cmds.add(join(nodes, ","));
		}
		cmds.add("-p");

		exec(cmds.toArray(new String[0])); 

	}

	public static void activateQueue(String queueName, Collection<NodeInfo> infos, String [] users) 
	throws ReserveException {
		String [] nodes = getNodeNames(infos);
		if (users.length == 0)  //nothing to do
			throw new ReserveException("No user specified");
		if (nodes.length == 0){  //nothing to do
			logger.info("deactivateQueue: No node specified. ignore.");
			return;			
		}
		exec(PLUS_CONDOR_CONTROL, "--start", 
				"-r", queueName, 
				"-u", join(users, ","), 
				"-n", join(nodes, ","),
				"-p"
		); 
		
	}
	
	
	
}
